今天我們開始來探討,如何能夠有邏輯地來產生node
及element Entity
,而不是每需要一個Entity
就直接呼叫一次creator function
。
今天先改進Plate
,[Day14]
繼續討論如何改進box
。
目前的Plate
是由單個QUAD4 element
構成。
QUAD4 element
呢?QUAD4 element
呢?很多次如果...
之後,是不是就可以做出一個網格大小合理的平板了呢?
有了初步的想法之後,我們來思考一下如何執行? 我們希望能夠寫出一個function
,在給定x長度
、y長度
、x及y方向各自元素個數
後,能回傳所有的座標(我們稱作grid
)。
我們的解法是使用numpy
的meshgrid function
來產生(x, y)
座標,z
座標則由numpy
的ones function
產生,我們於其後乘以一個z_elv
,用以調整平板的高度(即調整z
座標)。
最後,我們還提供了兩個微調的功能,分別是調整旋轉角度及將原點對準某個(x, y)
座標(即平移整個平板)。其中旋轉的功能比較難,我們參考了Stack Overflow
的作法,使用了scipy.spatial.transform
內的Rotation.from_rotvec function
。
# gen_seqs.py
from scipy.spatial.transform import Rotation as scipy_rotation
def _gen_one_layer_grids(l, w, en1, en2, *, z_elv=None, move_xy=None, rot_angle=None):
m1, m2 = en1+1, en2+1
x = np.linspace(0, l, m1)
y = np.linspace(0, w, m2)
X, Y = np.meshgrid(x, y)
x_shape = X.shape
z_elv = z_elv or 0
Z = np.ones(x_shape)*z_elv
if rot_angle is not None:
XYZ = np.array([X.ravel(), Y.ravel(), Z.ravel()]).transpose()
r = scipy_rotation.from_rotvec(
rot_angle*np.array([0, 0, 1]), degrees=True)
XYZrot = r.apply(XYZ)
X = XYZrot[:, 0].reshape(x_shape)
Y = XYZrot[:, 1].reshape(x_shape)
Z = XYZrot[:, 2].reshape(x_shape)
if move_xy is not None:
ox, oy = move_xy
X += ox
Y += oy
yield from (X.flat, Y.flat, Z.flat)
接下來我們用gen_shell_grids
來幫_gen_one_layer_grids
做一個轉置,這樣就可以得到每一個grid
的座標值了。
# gen_seqs.py
def gen_shell_grids(l, w, en1, en2, *, z_elv, move_xy, rot_angle):
return zip(*_gen_one_layer_grids(l, w, en1, en2,
z_elv=z_elv,
move_xy=move_xy,
rot_angle=rot_angle))
Plate Seqs
這個部份就需要大家發揮一點想像力。
我們知道LS-DYNA的QUAD4 element
是逆時針建立其四個node
,所以我們想要達成的是:
x
方向,不斷逆時針寫出每個QUAD4 element
的四個node id
,直到x
方向寫到了指定個數。y
座標往上走一層,繼續往x
方向寫指定個數,直到y
方向也寫到指定個數為止。# gen_seqs.py
def gen_shell_seqs(en1, en2, *, start=1):
en = en1*en2
cnt, total_cnt = 0, 0
while True:
if total_cnt == en:
break
if cnt == en1:
cnt = 0
else:
n1, n2, n3, n4 = start, start+1, start+en1+2, start+en1+1
total_cnt += 1
cnt += 1
yield (n1, n2, n3, n4)
start += 1
舉例來說,這邊我們想要建立一個x
方向有2
個element
,y
方向有2
個element
的plate
。
en1, en2 = 2, 2
list(gen_shell_seqs(en1, en2))
其輸出為:
list(gen_shell_seqs(en1, en2))=[(1, 2, 5, 4), (2, 3, 6, 5), (4, 5, 8, 7), (5, 6, 9, 8)]
由於gen_shell_seqs
是一個generator
,所以需要使用list
才能print
出其內的元素。
有了gen_shell_grids
與gen_shell_seqs
之後,我們可以開始來實作create_plate_nodes function
。
create_plate_nodes
先透過gen_shell_grids
取得shell_grids
的generator
。list comprehensions
配合create_node
及上述generator
,產生了node Entities
,並收集為一list
。gen_shell_seqs
,得到一個generator
,作為下一步create_plate_q4shells
的準備。node Entities
及shell_seqs
。# creators.py
def create_plate_nodes(l,
w,
en1,
en2,
node_start_id,
z_elv=None,
move_xy=None,
rot_angle=None,
deck=None):
deck = deck or constants.LSDYNA
shell_grids = gen_shell_grids(
l, w, en1, en2, z_elv=z_elv, move_xy=move_xy, rot_angle=rot_angle)
keys = ('NID', 'X', 'Y', 'Z')
nodes = [create_node(dict(zip(keys, (nid, *xyz))), deck=deck)
for nid, xyz in enumerate(shell_grids, start=node_start_id)]
shell_seqs = gen_shell_seqs(en1, en2, start=node_start_id)
return nodes, shell_seqs
create_plate_q4shells
的寫法與create_plate_nodes
差不多。我們透過create_plate_nodes
的第二個回傳值shell_seqs
配合create_shell
來產生QUAD4 Entities
,並收集為一list
後回傳。
# creators.py
def create_plate_q4shells(shell_seqs, shell_start_id, pid, deck=None):
deck = deck or constants.LSDYNA
keys = ('type', 'PID', 'EID', 'N1', 'N2', 'N3', 'N4')
type_ = ShellType.QUAD
return [create_shell(dict(zip(keys, (type_, pid, eid, *ns))), deck=deck)
for eid, ns in enumerate(shell_seqs, start=shell_start_id)]